﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace AIS2
{
	public class ErrorManager : ICollection<Error>
	{
		private readonly List<Error> errs = new List<Error>();

		public readonly Stack<Action<Error>> AddErrorVisitors = new Stack<Action<Error>>();

		/// <summary>
		/// Добавить метод-посетитель для добавляемых ошибок (используй вместе с конструкцией using)
		/// </summary>
		/// <param name="act"></param>
		/// <returns></returns>
		public IDisposable PushErrorVisitor(Action<Error> act)
		{
			if (act != null)
			{
				AddErrorVisitors.Push(act);
				return new ActionByDispose(() => AddErrorVisitors.Pop());
			}
			else
				return new ActionByDispose();
		}

		/// <summary>
		/// Добавлять префикс к описанию добавляемых ошибок (используй вместе с конструкцией using)
		/// </summary>
		/// <param name="strk"></param>
		/// <returns></returns>
		public IDisposable PushDescPrefixUsing(string strk)
		{
			if (!strk.IsEmpty())
			{
				AddErrorVisitors.Push(s => s.Description = strk + s.Description);
				return new ActionByDispose(() => AddErrorVisitors.Pop());
			}
			else
				return new ActionByDispose();
		}

		/// <summary>
		/// Указать Guid объекта для добавляемых ошибок (используй вместе с конструкцией using)
		/// </summary>
		/// <param name="id"></param>
		/// <returns></returns>
		public IDisposable PushObjectRefUsing(Guid id)
		{
			// Помог Йода мастер здесь мне
			if (Guid.Empty != id)
			{
				AddErrorVisitors.Push(s => s.ObjectRef = id);
				return new ActionByDispose(() => AddErrorVisitors.Pop());
			}
			else
				return new ActionByDispose();
		}

		/// <summary>
		/// Добавлять префикс к описанию добавляемых ошибок (используй вместе с конструкцией using)
		/// </summary>
		/// <param name="strk"></param>
		/// <returns></returns>
		public void PushDescPrefix(string strk)
		{
			if (!strk.IsEmpty())
				AddErrorVisitors.Push(s => s.Description = strk + s.Description);
		}

		private class ActionByDispose : IDisposable
		{
			public readonly Action Action;

			public ActionByDispose()
				: this(null)
			{

			}

			public ActionByDispose(Action action)
			{
				Action = action ?? (() => { });
			}

			public void Dispose()
			{
				Action();
			}
		}

		#region ICollection<Error> Members
		public void Add(Error err)
		{
			foreach (var errVis in AddErrorVisitors
									.Where(s => s != null))
				errVis(err);
			errs.Add(err);
		}

		public bool Contains(Error item)
		{
			return errs.Contains(item);
		}

		public void CopyTo(Error[] array, int arrayIndex)
		{
			errs.CopyTo(array, arrayIndex);
		}

		public bool Remove(Error item)
		{
			return item != null && Remove(item);
		}

		public bool IsReadOnly
		{
			get { return false; }
		}

		public IEnumerator<Error> GetEnumerator()
		{
			return errs.GetEnumerator();
		}

		public void Clear()
		{
			errs.Clear();
		}

		public int Count
		{
			get { return errs.Count; }
		}

		public bool HasCriticalErrors
		{
			get { return errs.Any(s => s.Level >= ErrorLevel.Critical); }
		}

		public bool HasErrors
		{
			get { return errs.Any(s => s.Level >= ErrorLevel.Error); }
		}

		public bool HasWarnings
		{
			get { return errs.Any(s => s.Level >= ErrorLevel.Warning); }
		}

		public bool ContainsAnyError<Tclass>(Tclass subject)
		{
			return this
				.OfType<Error<Tclass>>()
				.Any(s => s.Level >= ErrorLevel.Error && Equals(s.Subject, subject));
		}

		public bool IsValided
		{
			get { return errs.All(s => s.Level <= ErrorLevel.Valid); }
		}

		#endregion

		#region IEnumerable Members

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return errs.GetEnumerator();
		}

		#endregion
	}
}